/*
 * Copyright (c) 2005, The haXe Project Contributors
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

 /*
  
 EXAMPLE:

var im = GDImage.createFromJpeg("Creek.jpg");
im.resize(300, 200);
im.crop(10, 20, 300, 150);

im.drawing.filledEllipse(10, 10, 50, 50, 0xff0000);
im.text.write(5, 100, 100, "hello", 0xffffff);

Web.setHeader("content-type", GD.imageTypeToMimeType(GDImageType.JPG));
im.outputJpeg();
 
 */

 
package hxphp.oo.gd;

import php.Lib;

class GDResource { }

class GDImage 
{
	public var resource:GDResource;
	public var drawing:GDDrawing;
	public var text:GDText;
	
	public var width(sX, null):Int;
	public var height(sY, null):Int;
	
	private function new(resource:GDResource) 
	{
		if (resource == null) throw "Error loading Image";
		
		this.resource = resource;
		drawing = new GDDrawing(resource);
		text = new GDText(resource);
		setReferences();
	}
	
	private function setReferences()
	{
		drawing.resource = resource;
		text.resource = resource;
	}
	
	
	/**************** STATIC FUNCTIONS USED TO MAKE IMAGES ********************/
	
	
	/** returns an image identifier representing a blank image of size x_size by y_size. */
	public static function create( x_size:Int, y_size:Int):GDImage 
	{
		var img = untyped __call__("imagecreate", x_size, y_size);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** create image from a GIF file */
	public static function createFromGif(filename:String):GDImage
	{
		var img = untyped __call__("imagecreatefromgif", filename);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** create image from a JPG file */
	public static function createFromJpeg(filename:String):GDImage
	{
		var img = untyped __call__("imagecreatefromjpeg", filename);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** create image from a PNG file */
	public static function createFromPng(filename:String):GDImage
	{
		var img = untyped __call__("imagecreatefrompng", filename);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** create image from a String */
	public static function createFromString(filename:String):GDImage
	{
		var img = untyped __call__("imagecreatefromstring", filename);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** create image from a WBMP file */
	public static function createFromBmp(filename:String):GDImage
	{
		var img = untyped __call__("imagecreatefromwbmp", filename);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** create image from a XBM file */
	public static function createFromXbm(filename:String):GDImage
	{
		var img = untyped __call__("imagecreatefromxbm", filename);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** create image from a XPM file */
	public static function createFromXpm(filename:String):GDImage
	{
		var img = untyped __call__("imagecreatefromxpm", filename);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	/** returns an image identifier representing a blank image of size x_size by y_size. */
	public static function createTrueColor ( x_size:Int, y_size:Int):GDImage 
	{
		var img = untyped __call__("imagecreatetruecolor", x_size, y_size);
		
		return new GDImage((Std.is(img, Bool)) ? null : img);
	}
	
	
	/***************************************************************************************/
	
	
	
	
	
	
	/** allows for two different modes of drawing on truecolor images */
	public function alphaBlending( blendmode:Bool ):Bool
	{
		return untyped __call__("imagealphablending ", resource, blendmode);
	}

	
	/** returns a color identifier representing the color composed of the given RGB components*/
	public function colorAllocate( red:Int, green:Int, blue:Int ):Int
	{
		return untyped __call__("imagecolorallocate", resource, red, green, blue);
	}
	
	/** behaves identically to imagecolorallocate() with the addition of the transparency */
	public function colorAllocateAlpha( red:Int, green:Int, blue:Int, alpha:Int ):Int
	{
		return untyped __call__("imagecolorallocatealpha", resource, red, green, blue, alpha);
	}
	
	/** Returns the index of the color of the pixel at the specified location in the image specified by image. */
	public function colorAt( x:Int, y:Int ):Int
	{
		return untyped __call__("imagecolorat", resource, x, y);
	}

	public function colorClosest( red:Int, green:Int, blue:Int ):Int 
	{
		return untyped __call__("imagecolorclosest", resource, red, green, blue);
	}
	
	public function colorClosestAlpha( red:Int, green:Int, blue:Int, alpha:Int ):Int 
	{
		return untyped __call__("imagecolorclosestalpha", resource, red, green, blue, alpha);
	}
	
	/** function de-allocates a color previously allocated with imagecolorallocate() or imagecolorallocatealpha(). */
	public function colorDeallocate( color:Int ):Int 
	{
		return untyped __call__("imagecolordeallocate", resource, color);
	}
	
	/** Returns the index of the specified color in the palette of the image. */
	public function colorExact( red:Int, green:Int, blue:Int ):Int 
	{
		return untyped __call__("imagecolorexact", resource, red, green, blue);
	}
	
	/** Returns the index of the specified color in the palette of the image. */
	public function colorExactAlpha ( red:Int, green:Int, blue:Int, alpha:Int  ):Int 
	{
		return untyped __call__("imagecolorexact", resource, red, green, blue, alpha);
	}
	
	/** Makes the colors of the palette version of an image more closely match the true color version  */
	public function colorMatch( image2:GDImage ):Bool 
	{
		return untyped __call__("imagecolormatch", resource, image2.resource);
	}
	
	/** This function is guaranteed to return a color index for a requested color, either the exact color or the closest possible alternative.  */
	public function colorResolve( red:Int, green:Int, blue:Int):Int 
	{
		return untyped __call__("imagecolorresolve", resource, red, green, blue);
	}
	
	/** This function is guaranteed to return a color index for a requested color, either the exact color or the closest possible alternative. with alpha */
	public function colorResolveAlpha( red:Int, green:Int, blue:Int, alpha:Int  ):Int 
	{
		return untyped __call__("imagecolorresolvealpha", resource, red, green, blue, alpha);
	}
	
	/** This function is guaranteed to return a color index for a requested color, either the exact color or the closest possible alternative.  */
	public function colorSet( index:Int, red:Int, green:Int, blue:Int):Void 
	{
		untyped __call__("imagecolorset", resource, index, red, green, blue);
	}
	
	/** This returns a ColorDef with red, green, blue and alpha keys that contain the appropriate values for the specified color index.  */
	public function colorsForIndex (index:Int):GDColorDef 
	{
		var a = untyped __call__("imagecolorsforindex", resource, index);
		
		var a2 = Lib.hashOfAssociativeArray(a);
		
		return { red:a2.get('red'), green:a2.get('green'), blue:a2.get('blue'), alpha:a2.get('alpha') };
	}
	
	/** This returns the number of colors in the specified image's palette or 0 for truecolor images. */
	public function colorsTotal ( ):Int 
	{
		return untyped __call__("imagecolorstotal", resource);
	}
	
	/** This returns the number of colors in the specified image's palette or 0 for truecolor images. */
	public function colorTransparent ( color:Int ):Int 
	{
		return untyped __call__("imagecolortransparent", resource, color);
	}
	
	/** undocumented */
	public function convolution ( matrix3x3:Array<Dynamic>, div:Float, offset:Float ):Int 
	{
		return untyped __call__("imageconvolution", resource, matrix3x3, div, offset);
	}
	
	/** Quick Resize the image - only works properly with truecolor images */
	public function resize(width:Int, height:Int)
	{
		var originalImage = resource;
		var newImage = untyped __call__("imagecreatetruecolor", width, height);
		
		untyped __call__("imagecopyresampled", newImage, originalImage, 0, 0, 0, 0, width, height, sX(), sY());
		
		resource = newImage;
		setReferences();
		
		untyped __call__("imagedestroy", originalImage);
	}
	
	/** Quick Crop the image - only works properly with truecolor images */
	public function crop(x:Int, y:Int , width:Int, height:Int)
	{
		var originalImage = resource;
		var newImage = untyped __call__("imagecreatetruecolor", width, height);
		
		untyped __call__("imagecopyresampled", newImage, originalImage, 0, 0, x, y, sX(), sY(), sX(), sY());
		
		resource = newImage;
		setReferences();
		
		untyped __call__("imagedestroy", originalImage);
	}
	
	

	
	/** Copy a part of src_im onto dst_im starting at the x,y coordinates src_x, src_y with a width of src_w and a height of src_h. The portion defined will be copied onto the x,y coordinates, dst_x and dst_y. */
	public function copy( src_im:GDImage, dst_x:Int, dst_y:Int, src_x:Int, src_y:Int, src_w:Int, src_h:Int ):Bool 
	{
		return untyped __call__("imagecopy", resource, src_im.resource, dst_x, dst_y, src_x, src_y, src_w, src_h);
	}
	
	/** same as iamgeCopy with pcr (percent of alpha) two images will be merged according to pct which can range from 0 to 100. */
	public function copyMerge( src_im:GDImage, dst_x:Int, dst_y:Int, src_x:Int, src_y:Int, src_w:Int, src_h:Int, pct:Int ):Bool 
	{
		return untyped __call__("imagecopymerge", resource, src_im.resource, dst_x, dst_y, src_x, src_y, src_w, src_h, pct);
	}
	
	/**  */
	public function copyMergeGray( src_im:GDImage, dst_x:Int, dst_y:Int, src_x:Int, src_y:Int, src_w:Int, src_h:Int, pct:Int ):Bool 
	{
		return untyped __call__("imagecopymergegray", resource, src_im.resource, dst_x, dst_y, src_x, src_y, src_w, src_h, pct);
	}
	
	/** imagecopyresampled() copies a rectangular portion of one image to another image, smoothly interpolating pixel values */
	public function copyResampled( src_im:GDImage, dst_x:Int, dst_y:Int, src_x:Int, src_y:Int, dst_w:Int, dst_h:Int, src_w:Int, src_h:Int ):Bool 
	{
		return untyped __call__("imagecopyresampled", resource, src_im.resource, dst_x, dst_y, src_x, src_y, dst_w, dst_h, src_w, src_h);
	}
	
	/**  */
	public function copyResized ( src_im:GDImage, dst_x:Int, dst_y:Int, src_x:Int, src_y:Int, dst_w:Int, dst_h:Int, src_w:Int, src_h:Int ):Bool 
	{
		return untyped __call__("imagecopyresized", resource, src_im.resource, dst_x, dst_y, src_x, src_y, dst_w, dst_h, src_w, src_h);
	}
	
	
	
	
	
	
	public function destroy():Bool
	{
		return untyped __call__("imagedestroy", resource);
	}
	
	/** Apply a gamma correction to a GD image */
	public function gammaCorrect( inputgamma:Float, outputgamma:Float )
	{
		return untyped __call__("imagegammacorrect", resource, inputgamma, outputgamma );
	}
	
	
	/** Enable or disable interlace */
	public function interlace( ?interlace:Int ):Int
	{
		return untyped __call__("imageinterlace", resource, interlace);
	}
	
	/** Finds whether an image is a truecolor image  */
	public function isTrueColor ( ):Bool
	{
		return untyped __call__("imageistruecolor ", resource );
	}
	
	
	/** Set the alpha blending flag to use the bundled libgd layering effects  */
	public function imageLayerEffect ( effect:GDImageLayerEffect ):Bool
	{
		var filters = [GDImageLayerEffect.REPLACE, GDImageLayerEffect.NORMAL, GDImageLayerEffect.OVERLAY];
		
		var c = 0;
		for ( i in filters)
		{
			if (i == effect) break;
			c++;
		}
		
		return untyped __call__("imagelayereffect", resource, c);
	}
	
	/** Copy the palette from one image to another  */
	public function imagePaletteCopy ( source:GDImage ):Void
	{
		untyped __call__("imagepalettecopy", resource, source.resource);
	}
	
	/** Rotate an image with a given angle */
	public function imageRotate ( angle:Float, bgd_color:Int, ?ignore_transparent:Int):Void
	{
		resource = untyped __call__("imagerotate", resource, angle, bgd_color, ignore_transparent);
	}
	
	/** Set the flag to save full alpha channel information (as opposed to single-color transparency) when saving PNG images  */
	public function imageSaveAlpha( saveflag:Bool ):Bool
	{
		return untyped __call__("imagesavealpha ", resource, saveflag );
	}
	
	/** Get image width  */
	public function sX  ( ):Int
	{
		return untyped __call__("imagesx", resource);
	}
	
	/** Get image height  */
	public function sY  ( ):Int
	{
		return untyped __call__("imagesy", resource);
	}
	
	/** Convert a true color image to a palette image  */
	public function trueColorToPalette ( dither:Bool, ncolors:Int ):Bool
	{
		return untyped __call__("imagetruecolortopalette", resource, dither, ncolors);
	}
	
	
	/** creates the WBMP file in filename from the image image */
	public function toWBMP( ?filename:String, threshold:Int):String
	{
		return untyped __call__("image2wbmp", resource, filename, threshold);
	}
	
	/** applies the given filter filtertype  on the image . */
	public function filter(filtertype:GDImageFilter, ?arg1:Int, ?arg2:Int, ?arg3:Int):Bool
	{
		var filters = [GDImageFilter.NEGATE, GDImageFilter.GRAYSCALE, GDImageFilter.BRIGHTNESS, GDImageFilter.CONTRAST, GDImageFilter.COLORIZE, GDImageFilter.EDGEDETECT, GDImageFilter.EMBOSS, GDImageFilter.GAUSSIAN_BLUR, GDImageFilter.SELECTIVE_BLUR, GDImageFilter.MEAN_REMOVAL, GDImageFilter.SMOOTH];
		
		var c = 0;
		for ( i in filters)
		{
			if (i == filtertype) break;
			c++;
		}
		
		return untyped __call__("imagefilter", resource, c, arg1, arg2, arg3);
	}
	
	
	
	/************* OUTPUT THE IMAGE *********************/
	
	/** Output GD2 image to browser or file */
	public function  outputGD2( ?filename:String, ?chunk_size:Int, type:Int):Bool
	{
		return untyped __call__("imagegd2", resource, filename, chunk_size, type );
	}
	
	/** Output GD image to browser or file */
	public function  outputGD( ?filename:String):Bool
	{
		return untyped __call__("imagegd", resource, filename );
	}
	
	/** Output image to browser or file as GIF */
	public function outputGif ( ?filename:String ):Bool
	{
		return untyped __call__("imagegif", resource, filename);
	}
	
	/** Output image to browser or file as JPEG */
	public function outputJpeg ( ?filename:String=null, ?quality:Int=80 ):Bool
	{
		return untyped __call__("imagejpeg", resource, filename, quality);
	}
	
	/** Output image to browser or file as PNG  */
	public function outputPng ( ?filename:String ):Bool
	{
		return untyped __call__("imagepng", resource, filename);
	}
	
	/** Output image to browser or file as WBMP  */
	public function outputWBmp ( ?filename:String, ?foreground:Int ):Bool
	{
		return untyped __call__("imagewbmp", resource, filename, foreground);
	}
	
	/** Output image to browser or file as WBMP  */
	public function outputXbm ( ?filename:String, ?foreground:Int ):Bool
	{
		return untyped __call__("imagexbm", resource, filename, foreground);
	}
}

typedef GDColorDef = {
	var red:Int;
	var green:Int;
	var blue:Int;
	var alpha:Int;
}

enum GDImageLayerEffect
{
	REPLACE;
	NORMAL;
	OVERLAY;
}

enum GDImageFilter
{
	NEGATE;
	GRAYSCALE;
	BRIGHTNESS;
	CONTRAST;
	COLORIZE;
	EDGEDETECT;
	EMBOSS;
	GAUSSIAN_BLUR;
	SELECTIVE_BLUR;
	MEAN_REMOVAL;
	SMOOTH;
}

